<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>二维码矫正与清晰度检测</title>
  <style>
    body {
      font-family: sans-serif;
      padding: 20px;
    }

    canvas {
      border: 1px solid #ccc;
      margin-top: 10px;
    }

    #result {
      margin-top: 15px;
      font-size: 16px;
    }
  </style>
</head>

<body>

  <h2>📷 上传二维码图片进行矫正与清晰度检测</h2>
  <input type="file" id="fileInput" accept="image/*">
  <div id="result"></div>

  <canvas id="inputCanvas"></canvas>
  <canvas id="outputCanvas"></canvas>

  <!-- 外部库 -->
  <script src="./plugins/jsQR.js"></script>
  <script src="./plugins/perspective-transform.min.js"></script>

  <script>
    const fileInput = document.getElementById("fileInput");
    const inputCanvas = document.getElementById("inputCanvas");
    const outputCanvas = document.getElementById("outputCanvas");
    const resultDiv = document.getElementById("result");

    fileInput.addEventListener("change", async (e) => {
      const file = e.target.files[0];
      if (!file) return;

      const img = new Image();
      img.src = URL.createObjectURL(file);
      await img.decode();

      const ctx = inputCanvas.getContext("2d");
      inputCanvas.width = img.width;
      inputCanvas.height = img.height;
      ctx.drawImage(img, 0, 0);

      const imageData = ctx.getImageData(0, 0, img.width, img.height);


      console.time()
      const qr = jsQR(imageData.data, imageData.width, imageData.height);

      if (!qr || !qr.location) {
        resultDiv.textContent = "❌ 未检测到二维码。";
        return;
      }

      const { topLeftCorner, topRightCorner, bottomRightCorner, bottomLeftCorner } = qr.location;

      // 计算二维码的平均宽度和高度作为目标尺寸
      const width1 = Math.hypot(topRightCorner.x - topLeftCorner.x, topRightCorner.y - topLeftCorner.y);
      const width2 = Math.hypot(bottomRightCorner.x - bottomLeftCorner.x, bottomRightCorner.y - bottomLeftCorner.y);
      const height1 = Math.hypot(bottomLeftCorner.x - topLeftCorner.x, bottomLeftCorner.y - topLeftCorner.y);
      const height2 = Math.hypot(bottomRightCorner.x - topRightCorner.x, bottomRightCorner.y - topRightCorner.y);

      const dstWidth = Math.round((width1 + width2) / 2);
      const dstHeight = Math.round((height1 + height2) / 2);


      console.log('dstSize', qr, dstWidth, dstHeight)

      const transform = PerspT(
        [topLeftCorner.x, topLeftCorner.y, topRightCorner.x, topRightCorner.y, bottomRightCorner.x, bottomRightCorner.y, bottomLeftCorner.x, bottomLeftCorner.y],
        [0, 0, dstWidth, 0, dstWidth, dstHeight, 0, dstHeight]
      );

      console.log('transform', transform)

      // 改进：添加边缘检查防止采样越界
      const outCtx = outputCanvas.getContext("2d");
      outputCanvas.width = dstWidth;
      outputCanvas.height = dstHeight;
      const outputImageData = outCtx.createImageData(dstWidth, dstHeight);

      for (let y = 0; y < dstHeight; y++) {
        for (let x = 0; x < dstWidth; x++) {

          const [srcX, srcY] = transform.transformInverse(x, y);

          // 计算四个最近邻像素的坐标
          const sx0 = Math.floor(srcX);
          const sy0 = Math.floor(srcY);
          const sx1 = Math.min(sx0 + 1, imageData.width - 1);
          const sy1 = Math.min(sy0 + 1, imageData.height - 1);

          // 计算小数部分
          const fx = srcX - sx0;
          const fy = srcY - sy0;

          // 检查边界
          if (sx0 >= 0 && sx1 < imageData.width && sy0 >= 0 && sy1 < imageData.height) {
            const dstIndex = (y * dstWidth + x) * 4;

            // 对每个通道进行双线性插值
            for (let c = 0; c < 4; c++) {
              const i00 = (sy0 * imageData.width + sx0) * 4 + c;
              const i01 = (sy0 * imageData.width + sx1) * 4 + c;
              const i10 = (sy1 * imageData.width + sx0) * 4 + c;
              const i11 = (sy1 * imageData.width + sx1) * 4 + c;

              const top = imageData.data[i00] * (1 - fx) + imageData.data[i01] * fx;
              const bottom = imageData.data[i10] * (1 - fx) + imageData.data[i11] * fx;
              outputImageData.data[dstIndex + c] = top * (1 - fy) + bottom * fy;
            }
          }
        }
      }

      outCtx.putImageData(outputImageData, 0, 0);

      console.timeEnd()

    });

  </script>

</body>

</html>